package org.xtext.runtime.plan;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.xtext.runtime.formula.Predicate;
import org.xtext.runtime.interpreter.ASTRAClass;
import org.xtext.runtime.interpreter.Agent;
import org.xtext.runtime.interpreter.Plan;
import org.xtext.runtime.term.Variable;
import org.xtext.runtime.util.Bindings;
import org.xtext.runtime.util.EvaluateTermsVisitor;
import org.xtext.runtime.util.Unifier;

public class PlanInvocationStep implements IPlanStep {
	private Predicate operation;
	private ASTRAClass clazz;
	private int state = 0;
	private static int counter = 0;
	private Bindings bindings;
	
	public PlanInvocationStep(ASTRAClass clazz, Predicate operation) {
		this.clazz = clazz;
		this.operation = operation;
	}
	
	@Override
	public IPlanStep apply(Bindings set) {
		return new PlanInvocationStep(clazz, (Predicate) operation.copy().apply(set));
	}

	@Override
	public void handle(Agent agent, PlanStack planStack) {
		switch(state) {
		case 0:
			Plan plan = clazz.getPlan(operation);
			if (plan == null) {
				agent.logAction(operation + "[NO SUCH PLAN]");
				planStack.fail();
				return;
			}
			
			plan = plan.copy();
			Set<Variable> vars = plan.getVariables();
			for (Variable var : vars) {
				var.applyRenaming("pla_" + (counter++) + "_");
			}

			// Generate a set of bindings for the operation -> plan label
			operation = (Predicate) operation.accept(new EvaluateTermsVisitor(agent));
			bindings = Unifier.unify(operation, plan.getLabel());

			planStack.back();
			planStack.addPlanStep(plan.getPlan().apply(bindings));
			planStack.getNextActivity().handle(agent, planStack);
			state = 1;
			break;
		case 1:
			// Reduce the bindings -> this removes chains of variables with variable->value pairs.
			bindings.reduce();
			state = 2;
		}
	}

	@Override
	public String toString(String tabs) {
		return tabs + operation;
	}
	
	public String toFullString(String tabs) {
		return toString(tabs);
	}
	
	@Override
	public Set<Variable> variables() {
		Set<Variable> variables = new HashSet<Variable>();
		variables.addAll(this.operation.variables());
		return variables;
	}

	@Override
	public IPlanStep copy(Map<String, Variable> vars) {
		return new PlanInvocationStep(clazz, operation.copy(vars));
	}

	@Override
	public void reset() {
	}
}
